/**
 * @file main.c
 * @brief framebuffer测试工程
 * @author wangkun (wangkun@csmicro.com)
 * @version 1.0
 * @date 2021-07-03
 * 
 * @copyright Copyright (c) 2021  武汉凌久微电子有限公司
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2021-07-03 <td>1.0     <td>wangh     <td>内容
 * </table>
 */
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fb.h>
#include <linux/ioctl.h>
#include <sys/mman.h>

struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;

char *frameBuffer = 0;

void printVarScreenInfo(struct fb_var_screeninfo vinfo);
void printFixedScreenInfo(struct fb_fix_screeninfo finfo);

void draw_rect_rgb16(int x0, int y0, int width, int height, int color);
void draw_rect_rgb32(int x0, int y0, int width, int height, int color);
void draw_line_rgb16(int x0, int y0, int width, int height, int color, int flag);
//void draw_line_rgb32(int x0, int y0, int width, int height, int color, int flag);

int main(int argc, char **argv)
{
    const char *dev_file = "/dev/fb0";
    //const char* dev_file = "/home/wk/gpu/framebuffer/build/test";

    long int screen_size = 0;
    int fb_fd = 0;

    fb_fd = open(dev_file, O_RDWR);

    if (fb_fd == -1)
    {
        perror("Error: cannot open framebuffer device\n");
        //exit(1);
        return -1;
    }
    else
    {
        printf("%s open success: %d\n", dev_file, fb_fd);
    }

    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) == -1)
    {
        perror("Error: reading fixed information\n");
        //exit(3);
        return -2;
    }

    printFixedScreenInfo(finfo);

    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) == -1)
    {
        perror("Error: reading variable information\n");
        //exit(3);
        return -3;
    }

    printVarScreenInfo(vinfo);

    screen_size = finfo.smem_len;

    frameBuffer = (char *)mmap(0, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (frameBuffer == MAP_FAILED)
    {
        perror("Error: failed to map framebuffer device to memory");
        return -4;
    }
    else
    {
        printf("framebuffer addr:0x%016x, addr:0x%016x\n", frameBuffer, &fb_fd);
    }

//    draw_line_rgb16(50, 80, 260, 0, 0xffff0000, 0);
//    draw_line_rgb16(160, 10, 0, 180, 0xff00ff00, 1); //可以画出一个交叉的十字，坐标都是自己设的。

//    draw_rect_rgb16(400, 400, 300, 300, 0xf800);
     while(1) {
    	draw_rect_rgb32(0, 0, 800, 600, 0x00ff0000);
    	sleep(1);
   	
    	draw_rect_rgb32(0, 0, 800, 600, 0x0000ff00);
    	sleep(1);

  	printf("draw line down\n");
    }

    munmap(frameBuffer, screen_size);

    close(fb_fd);

    return 0;
}

/**
 * @brief   打印fb驱动中var结构信息，注：fb驱动加载后，var结构可根据实际需要被重置
 * @param  screen_info      fb_var_screenifno
 */
void printVarScreenInfo(struct fb_var_screeninfo screen_info)
{
    printf("Variable screen info:\n"
           "\txres:%d\n"
           "\tyres:%d\n"
           "\txres_virtual:%d\n"
           "\tyres_virtual:%d\n"
           "\tyoffset:%d\n"
           "\txoffset:%d\n"
           "\tbits_per_pixel:%d\n"
           "\tgrayscale:%d\n"
           "\tred: offset:%2d, length: %2d, msb_right: %2d\n"       //这几个值跟颜色有关系
           "\tgreen: offset:%2d, length: %2d, msb_right: %2d\n"
           "\tblue: offset:%2d, length: %2d, msb_right: %2d\n"
           "\ttransp: offset:%2d, length: %2d, msb_right: %2d\n"
           "\tnonstd:%d\n"
           "\tactivate:%d\n"
           "\theight:%d\n"
           "\twidth:%d\n"
           "\taccel_flags:0x%x\n"
           "\tpixclock:%d\n"
           "\tleft_margin:%d\n"
           "\tright_margin: %d\n"
           "\tupper_margin:%d\n"
           "\tlower_margin:%d\n"
           "\thsync_len:%d\n"
           "\tvsync_len:%d\n"
           "\tsync:%d\n"
           "\tvmode:%d\n"
           "\n",
           screen_info.xres, screen_info.yres, screen_info.xres_virtual, screen_info.yres_virtual,
           screen_info.xoffset, screen_info.yoffset, screen_info.bits_per_pixel,
           screen_info.grayscale, screen_info.red.offset, screen_info.red.length,
           screen_info.red.msb_right, screen_info.green.offset, screen_info.green.length,
           screen_info.green.msb_right, screen_info.blue.offset, screen_info.blue.length,
           screen_info.blue.msb_right, screen_info.transp.offset, screen_info.transp.length,
           screen_info.transp.msb_right, screen_info.nonstd, screen_info.activate,
           screen_info.height, screen_info.width, screen_info.accel_flags, screen_info.pixclock,
           screen_info.left_margin, screen_info.right_margin, screen_info.upper_margin,
           screen_info.lower_margin, screen_info.hsync_len, screen_info.vsync_len,
           screen_info.sync, screen_info.vmode);
}

/**
 * @brief   打印fb驱动中var结构信息，注：fb驱动加载后，fix结构no重置
 * @param  finfo            固定screeninfo
 */
void printFixedScreenInfo(struct fb_fix_screeninfo finfo)
{
    printf("Fixed screen info:\n"
           "\tid: %s\n"
           "\tsmem_start:0x%lx\n"
           "\tsmem_len:%d\n"
           "\ttype:%d\n"
           "\ttype_aux:%d\n"
           "\tvisual:%d\n"
           "\txpanstep:%d\n"
           "\typanstep:%d\n"
           "\tywrapstep:%d\n"
           "\tline_length: %d\n"
           "\tmmio_start:0x%lx\n"
           "\tmmio_len:%d\n"
           "\taccel:%d\n"
           "\n",
           finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,
           finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,
           finfo.ywrapstep, finfo.line_length, finfo.mmio_start,
           finfo.mmio_len, finfo.accel);
}

/**
 * @brief   画大小为width*height的同色矩阵，5reds+6greens+5blues
 * @param  x0               draw start pos: x
 * @param  y0               draw start pos: y
 * @param  width            rect's width
 * @param  height           rect's height
 * @param  color            rect's fill color
 */
void draw_rect_rgb16(int x0, int y0, int width, int height, int color)
{
    const int bytesPerPixel = 2;
    const int stride = finfo.line_length / bytesPerPixel;
    const int red = (color & 0xff0000) >> (16 + 3);
    const int green = (color & 0xff00) >> (8 + 2);
    const int blue = (color & 0xff) >> 3;
    const short color16 = blue | (green << 5) | (red << (5 + 6));

    short *dest = (short *)(frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            dest[x] = color16;
        }
        dest += stride;
    }
}

/**
 * @brief   画直线，5reds+6greens+5blues
 * @param  x0               draw start pos: x
 * @param  y0               draw start pos: y
 * @param  width            rect's width
 * @param  height           rect's height
 * @param  color            rect's fill color
 * @param  flag             0: horizontal line, 1: vertical line
 */
void draw_line_rgb16(int x0, int y0, int width, int height, int color, int flag)
{
    const int bytesPerPixel = 2;                          //因为是rgb16，用16位来描述色深，所以2个字节
    const int stride = finfo.line_length / bytesPerPixel; //一行有多少个点
    const int red = (color & 0xff0000) >> (16 + 3);       //下面是颜色的操作，我目前还没弄明白
    const int green = (color & 0xff00) >> (8 + 2);
    const int blue = (color & 0xff) >> 3;
    const short color16 = blue | (green << 5) | (red << (5 + 6));

    short *dest = (short *)(frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); //这个就是我们画点的起始位置，其+stride就是换行（这个是我个人通过代码测试得出来的结论）

    int x = 0, y = 0;
    if (flag == 0)
    {
        for (x = 0; x < width; ++x) //width就是我们x方向的终点
        {
            dest[x] = color16;
        }
    }
    else if (flag == 1)
    {
        for (y = 0; y < height; y++) //height就是我们y方向的终点
        {
            dest[x] = color16; //这里x始终为0，和下面一句结合起来就是每一行就画一个点，一共画height行，不就是一条竖线了么，这里我还思考了很久。

            dest += stride;
        }
    }
}

/**
 * @brief    使用32位色画矩形               
 * @param  x0               矩形的左边界
 * @param  y0               矩形的上边界
 * @param  width            矩形的宽度
 * @param  height           矩形的高度
 * @param  color            颜色值
 */
void draw_rect_rgb32(int x0, int y0, int width, int height, int color)
{
    const int bytesPerPixel = 4;
    const int stride = finfo.line_length / bytesPerPixel;
/*
    const int red = (color & 0xff0000) >> (16);
    const int green = (color & 0xff00) >> (8);
    const int blue = (color & 0xff) ;
    const unsigned int color32 = blue | (green << 8) | (red << (16));
*/
    int *dest = (int *)(frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            dest[x] = color;
        }
        dest += stride;
    }
}
